package apis

import (
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/pem"
	"github.com/gin-gonic/gin"
	"github.com/go-admin-team/go-admin-core/sdk/api"
	"github.com/go-admin-team/go-admin-core/sdk/config"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"
	"tuowei.com/app/api/models"
	"tuowei.com/app/api/service/dto"
	"tuowei.com/pkg/xlog"

	"encoding/json"
	"errors"
	"github.com/dgrijalva/jwt-go"
)

var (
	secret = `-----BEGIN PRIVATE KEY-----
    your PRIVATE KEY-
-----END PRIVATE KEY-----`
	keyId        = "keyId"
	teamId       = "teamId"
	clientID     = "clientID" //网页授权登录填写的是Services Id，App端登录需要的是AppId
	appID        = "appID"
	redirectUrl  = ""
	authTokenUrl = "https://appleid.apple.com/auth/token"
)

type ApiAppleLogin struct {
	api.Api
}

func (e ApiAppleLogin) AuthLogin(c *gin.Context) {
	req := dto.AppleLoginReq{}
	err := e.MakeContext(c).
		MakeOrm().
		Bind(&req).
		Errors
	if err != nil {
		e.Logger.Error(err)
		e.Error(500, err, err.Error())
		return
	}
	_, appleAuthErr := AppleAuthToken(req.Code)
	if appleAuthErr != nil {
		e.Error(500, appleAuthErr, "登录失败")
		return
	}
	var student *models.SysStudent
	claim := StudentClaims{
		Student:        student,
		StandardClaims: jwt.StandardClaims{},
	}
	now := time.Now()
	claim.StandardClaims.IssuedAt = now.Unix()
	claim.StandardClaims.ExpiresAt = now.Add(time.Duration(config.JwtConfig.Timeout) * time.Second).Unix()
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
	tokenStr, err := token.SignedString([]byte(config.JwtConfig.Secret))
	if err != nil {
		xlog.Error(http.StatusInternalServerError, err, err.Error())
		return
	}

	e.Context.JSON(http.StatusOK, gin.H{
		"code":   http.StatusOK,
		"expire": now.Add(time.Duration(config.JwtConfig.Timeout) * time.Second),
		"token":  tokenStr,
	})
	return

}

// create client_secret
func GetAppleSecret() string {
	token := &jwt.Token{
		Header: map[string]interface{}{
			"alg": "ES256",
			"kid": keyId,
		},
		Claims: jwt.MapClaims{
			"iss": teamId,
			"iat": time.Now().Unix(),
			// constraint: exp - iat <= 180 days
			"exp": time.Now().Add(24 * time.Hour).Unix(),
			"aud": "https://appleid.apple.com",
			"sub": appID,
		},
		Method: jwt.SigningMethodES256,
	}

	ecdsaKey, _ := AuthKeyFromBytes([]byte(secret))
	ss, _ := token.SignedString(ecdsaKey)
	return ss
}

func AuthKeyFromBytes(key []byte) (*ecdsa.PrivateKey, error) {
	var err error

	var block *pem.Block
	if block, _ = pem.Decode(key); block == nil {
		return nil, errors.New("token: AuthKey must be a valid .p8 PEM file")
	}

	var parsedKey interface{}
	if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
		return nil, err
	}

	var pkey *ecdsa.PrivateKey
	var ok bool
	if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
		return nil, errors.New("token: AuthKey must be of type ecdsa.PrivateKey")
	}

	return pkey, nil
}

type AppleAuthTokenRes struct {
	Error        string `json:"error"`
	AccessToken  string `json:"access_token"`
	ExpiresIn    int    `json:"expires_in"`
	IDToken      string `json:"id_token"`
	RefreshToken string `json:"refresh_token"`
	TokenType    string `json:"token_type"`
}

func AppleAuthToken(code string) (*AppleAuthTokenRes, error) {
	form := url.Values{}
	form.Set("client_id", appID)
	form.Set("client_secret", GetAppleSecret())
	form.Set("code", code)
	form.Set("grant_type", "authorization_code")
	form.Set("redirect_uri", redirectUrl)

	var request *http.Request
	var err error
	if request, err = http.NewRequest("POST", authTokenUrl, strings.NewReader(form.Encode())); err != nil {
		return nil, err
	}
	request.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	var response *http.Response
	if response, err = http.DefaultClient.Do(request); nil != err {
		return nil, err
	}
	defer response.Body.Close()

	data, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, err
	}

	res := &AppleAuthTokenRes{}
	err = json.Unmarshal(data, res)
	if err != nil {
		return nil, err
	}

	if res.Error != "" {
		return res, errors.New(res.Error)
	}
	return res, nil
}
